﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GN.Pay.TradeChannel;
using Abp.Dependency;
using GN.Pay.Core.Runtime.Caching;
using GN.Pay.Core.Entities;
using GN.Pay.Core.Repositories;
using GN.Pay.Core.Startup;
using GN.Pay.Core.Exceptions;
using GN.Pay.Core.Runtime;
using GN.Pay.Utils.Security;
using GN.Framework.Extensions;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using GN.Pay.Core.Values.Entities;
using GN.Pay.Core.Values.Channel;
using GN.Pay.TradeChannel.Configure;

namespace GN.Pay.Core.Services.Impl
{
    /// <summary>
    /// 
    /// </summary>
    public class TradeCacheService : APayService, ITradeCacheService, Abp.Application.Services.IApplicationService
    {
        private readonly ICacheManager cacheManager;
        private readonly IIocManager iocManager;

        public TradeCacheService(ICacheManager cacheManager, IIocManager iocManager)
        {
            this.cacheManager = cacheManager;
            this.iocManager = iocManager;
        }

        /// <summary>
        /// 创建支付通道
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="channelId"></param>
        /// <returns></returns>
        private ITradeChannel CreateTradeChannel(string appId, string channelId)
        {
            var channelRepository = iocManager.Resolve<IMerchantAppChannelRepository>();
            var entity = channelRepository.FirstOrDefault(s => s.AppId == appId && s.ChannelId == channelId);
            return CreateTradeChannel(appId, entity);
        }

        /// <summary>
        /// 创建支付通道
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="channelId"></param>
        /// <returns></returns>
        private ITradeChannel CreateTradeChannel(string appId, MerchantAppChannel channel)
        {
            if (channel == null || channel.State != 1)
            {
                return null;
            }
            var installer = TradeChannelInstaller.Instance;
            var type = installer.GetTradeChannelType(channel.ChannelId);
            if (type == null)
            {
                PayExceptionUtils.ThrowPayValidationException("不支持支付通道 " + channel.ChannelId);
            }
            var app = GetMerchantAppInfo(appId);
            if (app == null)
            {
                return null;
            }
            ITradeChannel tradeChannel = (ITradeChannel)Activator.CreateInstance(type);
            var configData = RsaUtils.Decrypt(app.PrivateKey, channel.ConfigureData);
            var configInfo = configData.JsonToObject(tradeChannel.TradeChannelConfigureInfoType, true) as ITradeChannelConfigureInfo;
          //  tradeChannel.ChannelConfigure.SaveCheck(configInfo);
            tradeChannel.ChannelConfigure.ConfigureInfo = configInfo;
            return tradeChannel;
        }

        /// <summary>
        /// 获取交易通道
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="channelId"></param>
        /// <returns></returns>
        public ITradeChannel GetTradeChannel(string appId, string channelId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(appId, nameof(appId));
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(channelId, nameof(channelId));
            var cache = cacheManager.GetCache(CacheType.AppChannel);
            string cacheKey = MerchantAppChannel.CacheKey(appId, channelId);
            var base64 = cache.GetOrAdd(cacheKey, (key) =>
            {
                var channel = CreateTradeChannel(appId, channelId);
                if (channel == null)
                {
                    return null;
                }
                return Serialize(channel);
            });
            if (base64 == null)
            {
                return null;
            }
            try
            {
                return Deserialize<ITradeChannel>(base64);
            }
            catch
            {
                cache.Remove(cacheKey);
                return null;
            }
        }

        /// <summary>
        /// 移除交易通道
        /// </summary>
        /// <param name="appId">应用Id</param>
        /// <param name="channelId">通道id</param>
        public void RemoveTradeChannel(string appId, string channelId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(appId, nameof(appId));
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(channelId, nameof(channelId));
            var cache = cacheManager.GetCache(CacheType.AppChannel);
            cache.Remove(MerchantAppChannel.CacheKey(appId, channelId));
        }

        /// <summary>
        /// 获取通道集合
        /// </summary>
        /// <param name="appId">应用Id</param>
        /// <returns></returns>
        public List<ITradeChannel> GetTradeChannels(string appId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(appId, nameof(appId));
            var cache = cacheManager.GetCache(CacheType.AppChannels);
            var base64 = cache.GetOrAdd(appId, (key) =>
            {
                var channelRepository = iocManager.Resolve<IMerchantAppChannelRepository>();
                var channels = channelRepository.GetAll()
                    .Where(s => s.AppId == appId && s.State == 1).ToList();
                List<ITradeChannel> items = new List<ITradeChannel>();
                foreach (var item in channels)
                {
                    var channel = CreateTradeChannel(appId, item);
                    if (channel != null)
                    {
                        items.Add(channel);
                    }
                }
                return Serialize(items);
            });
            if (base64 == null)
            {
                return new List<ITradeChannel>();
            }
            try
            {
                return Deserialize<List<ITradeChannel>>(base64);
            }
            catch
            {
                cache.Remove(appId);
                return new List<ITradeChannel>();
            }
        }

        /// <summary>
        /// 移除通道集合
        /// </summary>
        /// <param name="appId">应用Id</param>
        /// <returns></returns>
        public void RemoveTradeChannels(string appId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(appId, nameof(appId));
            var cache = cacheManager.GetCache(CacheType.AppChannels);
            cache.Remove(appId);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private byte[] Serialize(object obj)
        {
            using (var ms = new System.IO.MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                return ms.ToArray();
            }
        }

        private T Deserialize<T>(byte[] base64)
        {
            using (var ms = new System.IO.MemoryStream(base64))
            {
                IFormatter formatter = new BinaryFormatter();
                return (T)formatter.Deserialize(ms);
            }
        }

        /// <summary>
        /// 获取商户应用
        /// </summary>
        /// <param name="appId">应用Id</param>
        /// <returns></returns>
        public MerchantAppCahceInfo GetMerchantAppInfo(string appId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(appId, nameof(appId));
            var cache = cacheManager.GetCache(CacheType.MerchantApp);
            var base64 = cache.GetOrAdd(appId, (key) =>
             {
                 var appRepository = iocManager.Resolve<IMerchantAppInfoRepository>();
                 var app = appRepository.Get(appId);
                 if (app == null || app.State != 1)
                 {
                     return null;
                 }
                 var merchantInfo = GetMerchantInfo(app.MerchantId);
                 if (merchantInfo == null)
                 {
                     return null;
                 }
                 var smsTemplates = app.SmsTemplates;
                 if (smsTemplates != null)
                 {
                     smsTemplates = smsTemplates.ToList();
                 }
                 else
                 {
                     smsTemplates = new List<MerchantAppSmsTemplate>();
                 }
                 var appSecurity = iocManager.Resolve<IAppSecurity>();
                 var info = appSecurity.Decrypt(app);
                 var appInfo = new MerchantAppCahceInfo()
                 {
                     AppHomeUrl = app.AppHomeUrl,
                     AppName = app.AppName,
                     Id = app.Id,
                     MerchantId = app.MerchantId,
                     PayDefaultNotifyUrl = app.PayDefaultNotifyUrl,
                     PayDefaultReturnUrl = app.PayDefaultReturnUrl,
                     RefundDefaultNotifyUrl = app.RefundDefaultNotifyUrl,
                     PrivateKey = info.PrivateKey,
                     PublicKey = info.PublicKey,
                     SignKey = info.SignKey,
                     AppKey = info.AppKey,
                     State = app.State,
                     CashierName = merchantInfo.CashierName,
                     LogoUrl = merchantInfo.LogoUrl,
                     MerchantName = merchantInfo.MerchantName,
                     MerchantType = merchantInfo.MerchantType,
                     SmsTemplates = smsTemplates
                 };
                 return Serialize(appInfo);
             });
            if (base64 == null)
            {
                return null;
            }
            try
            {
                return Deserialize<MerchantAppCahceInfo>(base64);
            }
            catch
            {
                cache.Remove(appId);
                return null;
            }
        }

        /// <summary>
        /// 移除商户应用
        /// </summary>
        /// <param name="appId">应用Id</param>
        /// <returns></returns>
        public void RemoveMerchantAppInfo(string appId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(appId, nameof(appId));
            var cache = cacheManager.GetCache(CacheType.MerchantApp);
            cache.Remove(appId);
        }

        /// <summary>
        /// 获取商户
        /// </summary>
        /// <param name="merchantId">商户Id</param>
        /// <returns></returns>
        public MerchantInfo GetMerchantInfo(string merchantId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(merchantId, nameof(merchantId));
            var cache = cacheManager.GetCache(CacheType.Merchant);
            var base64 = cache.GetOrAdd(merchantId, (key) =>
            {
                var mRepository = iocManager.Resolve<IMerchantInfoRepository>();
                var info = mRepository.Get(merchantId);
                if (info == null || info.State != 1)
                {
                    return null;
                }
                return Serialize(info);
            });
            if (base64 == null)
            {
                return null;
            }
            try
            {
                return Deserialize<MerchantInfo>(base64);
            }
            catch
            {
                cache.Remove(merchantId);
                return null;
            }
        }

        /// <summary>
        /// 移除商户
        /// </summary>
        /// <param name="merchantId">商户Id</param>
        /// <returns></returns>
        public void RemoveMerchantInfo(string merchantId)
        {
            PayExceptionUtils.CheckNotNullOrNotWhiteSpace(merchantId, nameof(merchantId));
            var cache = cacheManager.GetCache(CacheType.Merchant);
            cache.Remove(merchantId);
        }

        /// <summary>
        /// 设置付款返回通知
        /// </summary>
        /// <param name="notify">通知</param>
        public void SetPayReturnNotify(TradeNotify notify)
        {
            if (notify != null)
            {
                var cache = cacheManager.GetCache(CacheType.PayReturnNotify);
                cache.Set(notify.TradeNo, notify, new TimeSpan(0, 5, 0));
            }
        }

        /// <summary>
        /// 异步设置付款返回通知
        /// </summary>
        /// <param name="notify">通知</param>
        public async Task SetPayReturnNotifyAsync(TradeNotify notify)
        {
            if (notify != null)
            {
                var cache = cacheManager.GetCache(CacheType.PayReturnNotify);
                await cache.SetAsync(notify.TradeNo, notify, new TimeSpan(0, 5, 0));
            }
        }

        /// <summary>
        /// 获取付款返回通知
        /// </summary>
        /// <param name="tradeNo">交易号</param>
        /// <returns></returns>
        public TradeNotify GetPayReturnNotify(string tradeNo)
        {
            if (string.IsNullOrWhiteSpace(tradeNo))
            {
                return null;
            }
            var cache = cacheManager.GetCache(CacheType.PayReturnNotify);
            return cache.Get<TradeNotify>(tradeNo);
        }

        /// <summary>
        /// 异步获取付款返回通知
        /// </summary>
        /// <param name="tradeNo">交易号</param>
        /// <returns></returns>
        public async Task<TradeNotify> GetPayReturnNotifyAsync(string tradeNo)
        {
            if (string.IsNullOrWhiteSpace(tradeNo))
            {
                return null;
            }
            var cache = cacheManager.GetCache(CacheType.PayReturnNotify);
            return await cache.GetAsync<TradeNotify>(tradeNo);
        }

        /// <summary>
        /// 获取商户会员申请票据
        /// </summary>
        /// <param name="token">票据</param>
        /// <returns></returns>
        public MerchantMemberBankCardBindApplyToken GetMerchantMemberBankCardBindToken(string token)
        {
            if (string.IsNullOrWhiteSpace(token))
            {
                return null;
            }
            var cache = cacheManager.GetCache(CacheType.MerchantMemberBankCardBindToken);
            return cache.Get<MerchantMemberBankCardBindApplyToken>(token);
        }

        /// <summary>
        /// 设置商户会员申请票据
        /// </summary>
        /// <param name="bankCard">票据</param>
        public void SetMerchantMemberBankCardBindToken(string token, MerchantMemberBankCardBindApplyToken applyToken)
        {
            if (applyToken == null || string.IsNullOrWhiteSpace(token))
            {
                return;
            }
            var cache = cacheManager.GetCache(CacheType.MerchantMemberBankCardBindToken);
            cache.Set(token, applyToken, new TimeSpan(0, applyToken.SmsExpireMinute, 0));
        }

        /// <summary>
        /// 移除绑定
        /// </summary>
        /// <param name="token">票据</param>
        public void RemoveMerchantMemberBankCardBindToken(string token)
        {
            if (string.IsNullOrWhiteSpace(token))
            {
                return;
            }
            var cache = cacheManager.GetCache(CacheType.MerchantMemberBankCardBindToken);
            cache.Remove(token);
        }

        public ProtocolPaySmsCheck GetProtocolPaySmsCheck(string payId)
        {
            if (string.IsNullOrWhiteSpace(payId))
            {
                return null;
            }
            var cache = cacheManager.GetCache(CacheType.ProtocolPaySmsCheck);
            return cache.Get<ProtocolPaySmsCheck>(payId);
        }

        
        public void RemoveProtocolPaySmsCheck(string payId)
        {
            if (string.IsNullOrWhiteSpace(payId))
            {
                return;
            }
            var cache = cacheManager.GetCache(CacheType.ProtocolPaySmsCheck);
            cache.Remove(payId);
        }

        public void SetProtocolPaySmsCheck(string payId, ProtocolPaySmsCheck protocolPaySmsCheck)
        {
            if (protocolPaySmsCheck == null || string.IsNullOrWhiteSpace(payId))
            {
                return;
            }
            var cache = cacheManager.GetCache(CacheType.ProtocolPaySmsCheck);
            cache.Set(payId, protocolPaySmsCheck, new TimeSpan(0, protocolPaySmsCheck.SmsExpireMinute, 0));
        }

        private string GetSmsKey(SmsCheckCodeType checkCodeType, string key)
        {
            return checkCodeType.ToString() + "@" + key;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="checkCodeType"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetSmsCheckCode(SmsCheckCodeType checkCodeType, string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                return null;
            }
            var cache = cacheManager.GetCache(CacheType.SmsCheckCode);
            return cache.Get<string>(GetSmsKey(checkCodeType, key));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="checkCodeType"></param>
        /// <param name="key"></param>
        /// <param name="checkCodeHash"></param>
        public void SetSmsCheckCode(SmsCheckCodeType checkCodeType, string key, string checkCodeHash)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                return;
            }
            if (string.IsNullOrWhiteSpace(checkCodeHash))
            {
                return;
            }
            var cache = cacheManager.GetCache(CacheType.SmsCheckCode);
            cache.Set(GetSmsKey(checkCodeType, key), checkCodeHash, new TimeSpan(0, 5, 0));
        }

        /// <summary>
        /// 设置付款通知
        /// </summary>
        /// <param name="uniqueNo">>唯一号，交易号</param>
        public void SetPayNotify(string uniqueNo)
        {
            if (string.IsNullOrWhiteSpace(uniqueNo))
            {
                return;
            }
            var cache = cacheManager.GetCache(CacheType.IsPayNotify);
            cache.Set<bool>(uniqueNo, true, new TimeSpan(0, 0, 30));
        }

        /// <summary>
        /// 是否支付通知
        /// </summary>
        /// <param name="uniqueNo">唯一号，交易号</param>
        /// <returns></returns>
        public bool IsPayNotify(string uniqueNo)
        {
            if (string.IsNullOrWhiteSpace(uniqueNo))
            {
                return false;
            }
            var cache = cacheManager.GetCache(CacheType.IsPayNotify);
            return cache.Get<bool>(uniqueNo);
        }

    }
}
